import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;

//import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class PublicationServlet
 */

class PublicationTypes {
	private LinkedList<String[]> publicationTypes = new LinkedList<String[]>();
	private String xPath = "";
	private String nameSpaceURI = "";
	private String prefix = "";
	
	public void addPublicationTypes(String label, String types, String peerReviewed) {
		String token[] = {label.trim(), types.trim(), peerReviewed.trim()};
		this.publicationTypes.add(token);
	}
	
	public void addXpath(String path) {
		this.xPath = path;
	}
	
	public void addNameSpaceURI(String uri) {
		this.nameSpaceURI = uri;
	}
	
	public void addPrefix(String prefix) {
		this.prefix = prefix;
	}
	
	public String getPrefix() {
		return this.prefix;
	}
	
	public String getNameSpaceURI() {
		return this.nameSpaceURI;
	}
	
	public LinkedList<String[]> getAllPublicationTypes() {
		return this.publicationTypes;
	}
	
	public String getXpath() {
		return this.xPath;
	}
}

public class PublicationServlet extends HttpServlet {
	
	private static final long serialVersionUID = 1L;
	private static final String TempDirectory = "temp/";
	private static final String XSLDirectory   = "xsl/";
	private static final String ConfDirectory 	= "conf/";
	private static final String PLConfDirectory = "publicationlist/";
	private static final String ConfSuffix		= ".conf";
	private static final String ConfFile	  	= "conf.xml";
	private static final String MappingFile 	= "mapping/mapping-db.conf";
	private static final String TempFileSuffix = "pub";
	private static final String FS = System.getProperty("file.separator");
	private static final String URL = "url";
	private static final String Template = "template";
	private static final String EnableHyperLink = "enableHyperLink";
	private static final String ImageDivId = "repository_loadingimage_div";
	private static final String PublicationsDiv = "repository_publication_div";
	private static final String ImageSrc	  = "./images/ajax-loader.gif";
	// move into conf file?
	
	//publicationList configure names
	private static final String XPATH = "XPATH";
	private static final String PREFIX = "PREFIX";
	private static final String NAMESPACEURI = "NAMESPACEURI";
	
	private static final String PeerReviewed = "peerReviewed";
	private static final String Sort = "sort";
	private static final String Subsort = "subsort";
	private static final String Order = "order";
	private static final String Suborder = "suborder";
	private static final String DWLeftBracket = "";		//"document.write(\"";
	private static final String DWRightBracket = "";	//"\");";
	
	private static final String TokenHeader = "";
	private static final String TokenEnd = "#repository_searchscript_header#";
	
	private String repositoryInnerIframe	= "repository_inneriframe";
	
	
	private String Header = "";
	private String End = "";
	private String RepositoryURL = "";
	private String RepositoryURLTarget = "";
	private String MetaData = "";
	private String Fedora	= "";
	private String ServletContext = "";
	private String Delimiter	   = "";
	private String Stylesheet = "";
	
	private void clean() {
		this.MetaData = "";
		this.Fedora = "";
		this.ServletContext = "";
	}
	
	private PublicationTypes readPublicationListConfig(String config) throws MP2Exception {
		PublicationTypes types = new PublicationTypes();
		String defaultConf = "default.conf";
		char header = '@';
		if (!(new File(ServletContext + ConfDirectory + PLConfDirectory + config)).exists())
			config = defaultConf;
		BufferedReader br;
		try {
			br = new BufferedReader(new FileReader(ServletContext + ConfDirectory + PLConfDirectory + config));
			String line;
			String currLine[] = null;
			while ((line = br.readLine()) != null) {
				if (line.trim().isEmpty() || (line.trim().charAt(0) == '#'))
					continue;
				currLine = line.split("=", 2);
				if ((line.trim().charAt(0) == header) && (currLine.length == 2)) {
					if (currLine[0].trim().equals(header + XPATH))
						types.addXpath(currLine[1].trim());
					else if (currLine[0].trim().equals(header + PREFIX))
						types.addPrefix(currLine[1].trim());
					else if (currLine[0].trim().equals(header + NAMESPACEURI))
						types.addNameSpaceURI(currLine[1].trim());
					continue;
				}
				currLine = line.split("=", 3);
				if (currLine.length != 3)
					continue;
				types.addPublicationTypes(currLine[0].trim(), 
										  currLine[1].trim(), 
										  currLine[2].trim());
			}
		} catch (IOException e) {
			MP2Exception mp2 = new MP2Exception();
			throw mp2;
		}
		return types;
	}
	
	private String configure(HttpServletRequest request, PublicationListMetaData md) {
		String regex = "<|>";
		String element;
		String params[];
		String peerReviewed = "default";
		for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements(); ) {
			element = (String)e.nextElement();
			if (element.equalsIgnoreCase(Sort))
				md.setSorting(request.getParameter(element), request.getParameter(Order));			
			else if (element.equalsIgnoreCase(Subsort))
				md.setSubSorting(request.getParameter(element), request.getParameter(Suborder));
			
			else if (element.equalsIgnoreCase(Order) || element.equalsIgnoreCase(Suborder))
				;
			else if (element.equalsIgnoreCase(PeerReviewed))
				peerReviewed = PeerReviewed;
			else if (element.equalsIgnoreCase(URL))
				;
			else if (element.equalsIgnoreCase(EnableHyperLink))
				;
			else if (element.equalsIgnoreCase(Template)) {
				String template = request.getParameter(element).toLowerCase();
				md.setFiltersWildCards(element, template.replace(" and ", " & "));
			}
			else {
				params = request.getParameter(element).split(regex, 2);
				if (params.length > 1)
					md.setFilters(element, request.getParameter(element));					
				else 
					md.setFiltersWildCards(element, request.getParameter(element));
			}
		}
		return peerReviewed;
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) {
		this.requestHandler(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) {
		this.requestHandler(request, response);
	}
	
	private void readFromConfig() throws MP2Exception {		
		String rootTag 			= "module";
		String identifier 		= "name";
		String pointer	  		= "publicationlist";
		Configuration cf = new Configuration(ServletContext + FS + ConfDirectory + ConfFile, 
											 rootTag,
											 identifier,
											 pointer);		
		String stops[] = {"linktorepository", 
						  "linktorepositorytarget",
						  "metadata",
						  "fedora",
						  "stylesheet",
						  "xsldelimiter"};
		String results[];
		try {
			results = cf.readFromConfig(stops);
		} catch (Exception e) {
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.ReadingConfFileErr);
			e.printStackTrace();
			throw mp2;
		}
		RepositoryURL = results[0];
		RepositoryURLTarget = results[1];
		MetaData   	= results[2];
		Fedora	   	= results[3];
		Stylesheet 	= results[4];
		Delimiter	= results[5];
	}
	
	private String printException(String msg) {
		String labelLeft = "<h3>";
		String errorMsg	 = " Error found: ";
		String labelRight = "</h3>";
		return DWLeftBracket + labelLeft + errorMsg + labelRight + msg + DWRightBracket + printNewLine();
	}	

	private String addJavascriptCode(String iframeUrl) {
		String script = "<script type=\"text/javascript\">" + "\n";
		//Global Variables
		script += "var imgId='" + ImageDivId + "';" +  "\n" +
				  "var adjust=50;" +  "\n" +
				  "var browser;" +  "\n";
		// -> reset
		script += "function getBrowser() {" +  "\n" +
				  "var userAgent = window.navigator.userAgent.toLowerCase();" +  "\n" +
				  "if (userAgent.indexOf('msie 6') != -1)" +  "\n" +
				  "   browser='ie';" +  "\n" +
				  "else if (userAgent.indexOf('msie 7') != -1)" +  "\n" +
				  "   browser='ie';"+  "\n" +
				  "else if (userAgent.indexOf('opera') != -1)"  +  "\n" +
				  "   browser='opera';"  +  "\n" +
				  "else if (userAgent.indexOf('firefox') != -1)" +  "\n" +
				  "   browser='firefox';" +  "\n" +
			      "else if (userAgent.indexOf('safari') != -1)" +  "\n" +
				  "   browser='safari';" +  "\n" +
			      "else " +  "\n" +	
			      "browser='mozilla';" +  "\n" +
				  "}"  +  "\n" +
				  "function resize() {"	+ "\n" +
				  " var iframe=document.getElementById('" + repositoryInnerIframe + "');" + "\n" +
				  " var height=Math.max(document.body.scrollHeight, document.body.offsetHeight);" + "\n" +
				  " var width=Math.max(document.body.scrollWidth, document.body.offsetWidth);" + "\n" +
				  "  var original_url='" + iframeUrl + "';" + "\n" +
				  "  iframe.src = original_url+'?height='+height+'&width='+width;" + "\n" +
//				  " alert('height is '+height);" + "\n" +
				  "}" + "\n" +
				  "function resizeDiv() {"	+ "\n" +
				  " var iframe=document.getElementById('" + repositoryInnerIframe + "');" + "\n" +
				  " var div=document.getElementById('" + PublicationsDiv + "');" + "\n" +
				  " var height=div.scrollHeight+adjust;" + "\n" +
				  " var width=div.scrollWidth+adjust;" + "\n" +
				  //" if (browser=='ie' || browser=='safari'){" + "\n" +
				  //"  height=document.body.scrollHeight;" + "\n" +
				  //"  width =document.body.scrollWidth;" + "\n" +
				  //" } else {" + "\n" +
				  //"  height=document.body.offsetHeight+adjust;" + "\n" +
				  //"  width =document.body.offsetHeight+adjust;" + "\n" +
				  //" }" + "\n" +
				  "  var original_url='" + iframeUrl + "';" + "\n" +
				  "  iframe.src = original_url+'?height='+height+'&width='+width;" + "\n" +			
				  "}" + "\n" +
				  "function hideObj() {" + "\n" +
				  "	if (document.getElementById(imgId)) {" + "\n" +
				  "		var obj=document.getElementById(imgId);" + "\n" +
				  "		obj.style.display='none';" + "\n" +
				  "		obj.style.visibility='hidden';" + "\n" +
				  "	}" + "\n" +
				  "	resize();" + "\n" +
				  "}" + "\n" +
				  "</script>";
		return script;	
	}
	
	private String printTypeAsLabel(String label) {
		String labelLeft = "<h2><b>";
		String labelRight = "</b></h2>";
		return DWLeftBracket + labelLeft + capitalizedFstLetter(label) + labelRight + DWRightBracket + this.printNewLine();
	}
	
	private String capitalizedFstLetter(String input) {
		String inputs[] = input.split(" ");
		String result = "";
		for (int i = 0; i < inputs.length; i++) {
			result += Character.toUpperCase((inputs[i].charAt(0))) + inputs[i].substring(1) + " ";
		}
		return result;
	}
	
	private String decoratedWithJScript(String line) {		
		return DWLeftBracket + addQuotation(line) + DWRightBracket;
	}
	
	private String addQuotation(String line) {
		String regex = "\"";
		String replacement = "\\" + "\""; 
		return line.replace(regex, replacement);
	}
	
	private String printMargin() {
		return DWLeftBracket + "<br><br>" + DWRightBracket;
	}
	
	private String printNewLine() {
		return DWLeftBracket + "<br>" + DWRightBracket;
	}
	
	private void configureHeaderEnd() {
		
		//String LineBreak	= "<BR />";
		String tokenHeader = "<LI>";
		String tokenEnd	= "</LI>";
		
		Header = tokenHeader;
		End	= tokenEnd;
	}
	
	private void configureBufferSize(HttpServletResponse response) {
		response.setBufferSize(8 * 1024); // 8K buffer
	    response.setContentType("text/html");
	    response.setCharacterEncoding("UTF-8");
	}
	
	private String printHTML(String iframeUrl) {
		String html = "<head>" + "\n" +
					  this.addJavascriptCode(iframeUrl) + 
					  "</head>" + "\n";
			   html += "<body onload='resizeDiv()'>";
			   html += "<div id='"+ ImageDivId +"' align='center'>" + 
			   		   "<img src='" + ImageSrc + "'>" + 
			   		   "</div>";
			   html += "<div style='display:none;visibility:hidden;'><iframe src='" +iframeUrl+ "' id='"+repositoryInnerIframe+"'></iframe></div>";
			   html += "<div name='" + PublicationsDiv + "' id='" + PublicationsDiv + "'>";
		return html;
	}
	
	private String hideImageScript() {
		return "<script type=\"text/javascript\">hideObj();</script>";
	}
	
	private String resizeScript() {
		return "<script type=\"text/javascript\">resize();</script>";
	}
	
	private String printHTMLEnd() {
		return "</div>" + "</body>";
	}
	
	private void publicationListing(PublicationListMetaData md, HttpServletResponse response, 
									HttpServletRequest request, PublicationTypes pTypes) throws MP2Exception {
		try {
			
			boolean enableHyperLink = true;
			
			if (request.getParameter(EnableHyperLink) != null)
				enableHyperLink = (!request.getParameter(EnableHyperLink).equalsIgnoreCase("false"));
			
			PrintWriter pWriter = response.getWriter();
			this.configureHeaderEnd();
			this.configureBufferSize(response);

			md.setWebRoot(ServletContext);
			md.setXSLFile(ServletContext + XSLDirectory + Stylesheet);
			md.setConfFile(ServletContext + ConfDirectory + ConfFile);
			md.setMappingFile(ServletContext + ConfDirectory + MappingFile);
			md.setTempDirectory(ServletContext + TempDirectory);
			md.setTempFileSuffix(TempFileSuffix);
			md.setMetaData(this.MetaData);
			md.setFedora(this.Fedora);
			md.addHeader(TokenHeader);
			md.addFeet(TokenEnd);
			md.setupSearchModel();
			md.setupMetaDataModel();
			
			
			LinkedList<String[]> publicationTypes = pTypes.getAllPublicationTypes();
			
			md.setPeerReviewedXPath(pTypes.getXpath());
			md.setPeerReviewedPrefix(pTypes.getPrefix());
			md.setPeerReviewedNamespaceURI(pTypes.getNameSpaceURI());
						
			pWriter.println(this.printHTML(request.getParameter(URL)));
			response.flushBuffer();
			
			Iterator<String[]> iter = publicationTypes.iterator();
			
			
			while (iter.hasNext()) {
				String node[] = (String[]) iter.next();
				md.setPublicationType(node);
				String file = md.produce();
				this.writeToOutputFile(pWriter, file, node[0], md.getResults(), enableHyperLink);
				pWriter.println(this.resizeScript());
				response.flushBuffer();
				md.cleanCached();
			}
			pWriter.println(this.hideImageScript());
			pWriter.println(this.printHTMLEnd());
			response.flushBuffer();
			md.clean();
			
			pWriter.flush();
			pWriter.close();
			
		} catch (IOException e) {
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.PrintResultErr);
			e.printStackTrace();
			throw mp2;
		}
		
	}
	
	private void writeHyperLinkIn(PrintWriter pWriter, String content[], 
								  String pids[], boolean enableHyperLink) {
		String titleStart = "[" + Delimiter + "]";
		String titleStop  = "[/" + Delimiter + "]";
		
		for (int i = 0; i < content.length; i++) {
			String current = content[i];
			if (enableHyperLink) {
				current = current.replace(titleStart, "<a target='"+RepositoryURLTarget+"' href='" + RepositoryURL + pids[i] + "'>");
				current = current.replace(titleStop, "</a>");
			} else {
				current = current.replace(titleStart, "");
				current = current.replace(titleStop, "");
			}
			pWriter.println(Header + current + End);
		}
	}
	
	private void writeToOutputFile(PrintWriter pWriter, String outFile, 
								   String label, String pids[], boolean enableHyperLink) 
				throws MP2Exception  {
		BufferedReader br;
		if (outFile == null) {
			return;
		}
		try {
			br = new BufferedReader(new FileReader(outFile));
		} catch (FileNotFoundException e) {
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.OutPutFileNotFoundErr);
			e.printStackTrace();
			throw mp2;
		}
		if ((new File(outFile)).length() == 0)
			return;
		pWriter.println(this.printTypeAsLabel(label));
		String output = "";
		String line;
		try {
			while ((line = br.readLine()) != null) {
				//previous just added plain text, now have to enable hyperlink 
				//pWriter.println(this.decoratedWithJScript(line));
				output += line;
			}
		} catch (IOException e) {
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.ReadingOutPutFileErr);
			e.printStackTrace();
			throw mp2;
		}
		
		writeHyperLinkIn(pWriter, output.split(TokenEnd), pids, enableHyperLink);
		
		pWriter.println(this.printMargin());
	}
	
	private void requestHandler(HttpServletRequest request, HttpServletResponse response) {
		
		PublicationListMetaData md = new PublicationListMetaData();
		
		ServletContext = getServletContext().getRealPath("") + FS;
		String config = this.configure(request, md);
		PublicationTypes types = new PublicationTypes();
		try {
			this.readFromConfig();
			types = this.readPublicationListConfig(config + ConfSuffix);
		} catch (MP2Exception e) {
			//Throw the message as MP2Exception.
			this.printException(e.getMessage());
			MP2Exception mp2 = new MP2Exception();
			mp2.setMessage(MP2Exception.ReadingConfFileErr);			
			System.err.println(mp2.sendMessage());
			e.printStackTrace();
		}
		
		try {
			this.publicationListing(md, response, request, types);
		} catch (MP2Exception e) {
			this.printException(e.getMessage());
			System.err.println(e.getMessage());
			e.printStackTrace();
			return;
		}
		//System.out.println("-----> Get Publications and Print");
		//System.out.println();
		this.clean();
	}
}
